<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <script>
function defer() {
  const deferred = {};
  deferred.promise = new Promise((resolve, reject) => {
    deferred.resolve = resolve;
    deferred.reject = reject;
  });
  return deferred;
}

const _state = Symbol('state');
const _checkers = Symbol('checker');

class Signal {
  constructor(initState) {
    this[_state] = initState;
    this[_checkers] = new Map();
  }
  
  get state() {
    return this[_state];
  }

  set state(value) {
    // 每次状态变化时，检查未结束的 defer 对象
    [...this[_checkers]].forEach(([promise, {type, deferred, state}]) => {
      if(type === 'while' && value !== state // 当信号状态改变的时，while 信号结束
        || type === 'until' && value === state // 当信号状态改变为对应的 state 时，until 信号结束
      ) {
        deferred.resolve(value); 
        this[_checkers].delete(promise);
      }
    });
    this[_state] = value;
  }
  
  while(state) {
    const deferred = defer();
    if(state !== this[_state]) {
      // 如果当前状态不是 while 状态， while 的 deferred 结束
      deferred.resolve(this[_state]);
    }
    else {
      // 否则将它添加到 checkers 列表中等待后续检查
      this[_checkers].set(deferred.promise, {type: 'while', deferred, state});
    }
    return deferred.promise;
  }
  
  until(state) {
    const deferred = defer();
    if(state === this[_state]) {
      // 如果当前状态就是 until 状态， until 的 deferred 结束
      deferred.resolve(this[_state]);
    }
    else {
       // 否则将它添加到 checkers 列表中等待后续检查
      this[_checkers].set(deferred.promise, {type: 'until', deferred, state});
    }
    return deferred.promise;
  }
  
  delete(promise) {
    this[_checkers].delete(promise);
  }
}

const lucky = new Signal();

const timerID = setInterval(() => {
  const num = Math.floor(Math.random() * 10);
  console.log(`luck number: ${num}`);
  lucky.state = num;
}, 1000);

async function addLuckyBoy(name, num) {
  await lucky.until(num);
  console.log(`${name} is lucky boy!`);
  clearInterval(timerID);
}

addLuckyBoy('张三', 3);
addLuckyBoy('李四', 5);
addLuckyBoy('王五', 7);
  </script>
</body>
</html>